﻿using System;

namespace DistanceConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Program1.Main1();

            
            var c1 = new Degree(31.006808, 121.226956);
            var c2 = new Degree(31.222579, 121.305032);
            
            Console.WriteLine(CoordDispose.GetDistanceGoogle(c1, c2));
            Console.ReadLine();
            return;
            

            double a = CoordDispose.GetDistance(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918));//116.416984,39.944959
            double b = CoordDispose.GetDistanceGoogle(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918));

            var d = new Degree(116.412007, 39.947545);
            Degree[] dd = CoordDispose.GetDegreeCoordinates(d, 102);

            var cc = CoordDispose.GetRectRange(116.412007, 39.947545, 102);

            Console.WriteLine(a + " " + b);
            for (int index = 0; index < dd.Length; index++)
            {
                var degree = dd[index];
                var degree1 = cc[index];

                Console.WriteLine(degree.Logitude + "," + degree.Latitude + "=" + degree1.Logitude + "," + degree1.Latitude);

                Console.WriteLine(CoordDispose.GetDistance(degree, d));
                Console.WriteLine(CoordDispose.GetDistanceGoogle(degree, d));
                Console.WriteLine(CoordDispose.GetDistance(degree1, d));
                Console.WriteLine(CoordDispose.GetDistanceGoogle(degree1, d));
            }

          
        }
    }

    /// <summary>
    /// 经纬度坐标
    /// </summary>    

    public class Degree
    {
        public Degree(double x, double y)
        {
            Logitude = x;
            Latitude = y;
        }

        /// <summary>
        /// 经度
        /// </summary>
        public double Logitude { get; set; }

        /// <summary>
        /// 纬度
        /// </summary>
        public double Latitude { get; set; }
    }


    public class CoordDispose
    {
        private const double EARTH_RADIUS = 6378137.0;//地球半径(米)

        /// <summary>
        /// 角度数转换为弧度公式
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        private static double radians(double d)
        {
            return d * Math.PI / 180.0;
        }

        /// <summary>
        /// 弧度转换为角度数公式
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        private static double degrees(double d)
        {
            return d * (180 / Math.PI);
        }

        /// <summary>
        /// 计算两个经纬度之间的直接距离
        /// </summary>

        public static double GetDistance(Degree Degree1, Degree Degree2)
        {
            double radLat1 = radians(Degree1.Logitude);
            double radLat2 = radians(Degree2.Logitude);
            double a = radLat1 - radLat2;
            double b = radians(Degree1.Latitude) - radians(Degree2.Latitude);

            double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
             Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2)));
            s = s * EARTH_RADIUS;
            s = Math.Round(s * 10000) / 10000;
            return s;
        }

        /// <summary>
        /// 计算两个经纬度之间的直接距离(google 算法)
        /// </summary>
        public static double GetDistanceGoogle(Degree Degree1, Degree Degree2)
        {
            double radLat1 = radians(Degree1.Logitude);
            double radLng = radians(Degree1.Latitude);
            double radLat2 = radians(Degree2.Logitude);
            double radLng2 = radians(Degree2.Latitude);

            double s = Math.Acos(Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Cos(radLng - radLng2) + Math.Sin(radLat1) * Math.Sin(radLat2));
            s = s * EARTH_RADIUS;
            s = Math.Round(s * 10000) / 10000;
            return s;
        }

        /// <summary>
        /// 以一个经纬度为中心计算出四个顶点
        /// </summary>
        /// <param name="distance">半径(米)</param>
        /// <returns></returns>
        public static Degree[] GetDegreeCoordinates(Degree Degree1, double distance)
        {
            double dlng = 2 * Math.Asin(Math.Sin(distance / (2 * EARTH_RADIUS)) / Math.Cos(Degree1.Logitude));
            dlng = degrees(dlng);//一定转换成角度数  原PHP文章这个地方说的不清楚根本不正确 后来lz又查了很多资料终于搞定了

            double dlat = distance / EARTH_RADIUS;
            dlat = degrees(dlat);//一定转换成角度数

            return new Degree[] { new Degree(Math.Round(Degree1.Logitude + dlat,6), Math.Round(Degree1.Latitude - dlng,6)),//left-top
                                   new Degree(Math.Round(Degree1.Logitude - dlat,6), Math.Round(Degree1.Latitude - dlng,6)),//left-bottom
                                   new Degree(Math.Round(Degree1.Logitude + dlat,6), Math.Round(Degree1.Latitude + dlng,6)),//right-top
                                   new Degree(Math.Round(Degree1.Logitude - dlat,6), Math.Round(Degree1.Latitude + dlng,6)) //right-bottom
             };

        }

        private static void GetlatLon(double GLON, double GLAT, double distance, double angle, out double newLon, out double newLat)
        {
            double Ea = 6378137;     //   赤道半径  
            double Eb = 6356725;     //   极半径
            double dx = distance * Math.Sin(angle * Math.PI / 180.0);
            double dy = distance * Math.Cos(angle * Math.PI / 180.0);
            //double ec = 6356725 + 21412 * (90.0 - GLAT) / 90.0;

            // 21412 是赤道半径与极半径的差
            double ec = Eb + (Ea - Eb) * (90.0 - GLAT) / 90.0;
            double ed = ec * Math.Cos(GLAT * Math.PI / 180);
            newLon = (dx / ed + GLON * Math.PI / 180.0) * 180.0 / Math.PI;
            newLat = (dy / ec + GLAT * Math.PI / 180.0) * 180.0 / Math.PI;
        }

        public static Degree[] GetRectRange(double centorLogitude, double centorlatitude, double distance)
        {

            double temp = 0.0;
            double maxLatitude;
            double minLatitude;
            double maxLongitude;
            double minLongitude;
            GetlatLon(centorLogitude, centorlatitude, distance, 0, out temp, out maxLatitude);

            GetlatLon(centorLogitude, centorlatitude, distance, 180, out temp, out minLatitude);

            GetlatLon(centorLogitude, centorlatitude, distance, 90, out maxLongitude, out temp);

            GetlatLon(centorLogitude, centorlatitude, distance, 270, out minLongitude, out temp);
            maxLatitude = Math.Round(maxLatitude, 6);
            minLatitude = Math.Round(minLatitude, 6);
            maxLongitude = Math.Round(maxLongitude, 6);
            minLongitude = Math.Round(minLongitude, 6);
            return new Degree[] {
                new Degree(minLongitude,maxLatitude),//left-top
                new Degree(minLongitude,minLatitude),//left-bottom
                new Degree(maxLongitude,maxLatitude),//right-top
                new Degree(maxLongitude,minLatitude)  //right-bottom
             };

        }
    }
}